
from math import pi
import bpy
from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty
from mathutils import Vector, Matrix

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level
from sverchok.utils.curve import SvCircle
from sverchok.dependencies import circlify


class SvExCirclifyNode(SverchCustomTreeNode, bpy.types.Node):
    """
    Triggers: Circlify
    Tooltip: Generate circles packed into a larger circle
    """
    bl_idname = 'SvExCirclifyNode'
    bl_label = 'Circlify'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_CIRCLIFY'
    sv_dependencies = {'circlify'}

    major_radius : FloatProperty(
        name = "Major Radius",
        description = "The radius of the bigger circle, into which all other should be packed",
        default = 1.0,
        update = updateNode)

    planes = [
        ('XY', "XY", "XOY plane", 0),
        ('YZ', "YZ", "YOZ plane", 1),
        ('XZ', "XZ", "XOZ plane", 2)
    ]

    plane : EnumProperty(
        name = "Plane",
        description = "The coordinate plane in which the circles will be generated",
        items = planes,
        default = 'XY',
        update = updateNode)

    show_enclosure : BoolProperty(
        name = "Show enclosure",
        description = "Whether to generate the big circle, into which all other are inscribed",
        default = True,
        update = updateNode)

    def draw_buttons(self, context, layout):
        layout.prop(self, 'plane', expand=True)
        layout.prop(self, 'show_enclosure', toggle=True)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', 'Radiuses')
        d = self.inputs.new('SvVerticesSocket', "Center")
        d.use_prop = True
        d.default_property = (0.0, 0.0, 0.0)

        self.inputs.new('SvStringsSocket', "MajorRadius").prop_name = 'major_radius'
        self.outputs.new('SvCurveSocket', "Circles")
        self.outputs.new('SvVerticesSocket', "Centers")
        self.outputs.new('SvStringsSocket', "Radiuses")

    def to_2d(self, v):
        x,y,z = v
        if self.plane == 'XY':
            return x,y
        elif self.plane == 'YZ':
            return y,z
        else:
            return x,z

    def circle_to_curve(self, general_center, circle):
        x0 = circle.x
        y0 = circle.y
        if self.plane == 'XY':
            center = Vector((x0, y0, general_center[2]))
            matrix = Matrix.Translation(center)
        elif self.plane == 'YZ':
            center = Vector((general_center[0], x0, y0))
            matrix = Matrix.Rotation(pi/2, 4, 'Y')
            matrix.translation = center
        else:
            center = Vector((x0, general_center[1], y0))
            matrix = Matrix.Rotation(pi/2, 4, 'X')
            matrix.translation = center
        curve = SvCircle(matrix, circle.r)
        return curve

    def process(self):
        if not any(socket.is_linked for socket in self.outputs):
            return

        radiuses_s = self.inputs['Radiuses'].sv_get()
        radiuses_s = ensure_nesting_level(radiuses_s, 3)
        center_s = self.inputs['Center'].sv_get()
        center_s = ensure_nesting_level(center_s, 3)
        major_radius_s = self.inputs['MajorRadius'].sv_get()
        major_radius_s = ensure_nesting_level(major_radius_s, 2)

        curves_out = []
        centers_out = []
        radius_out = []
        for radiuses, centers, major_radiuses in zip_long_repeat(radiuses_s, center_s, major_radius_s):
            for radiuses, center, major_radius in zip_long_repeat(radiuses, centers, major_radiuses):
                center_2d = self.to_2d(center)
                enclosure = circlify.Circle(x=center_2d[0], y=center_2d[1], r=major_radius)
                circles = circlify.circlify(radiuses, target_enclosure=enclosure, show_enclosure=self.show_enclosure)
                curves = [self.circle_to_curve(center, circle) for circle in circles]
                curves_out.extend(curves)
                centers_out.append([tuple(curve.center) for curve in curves])
                radius_out.append([curve.radius for curve in curves])

        self.outputs['Circles'].sv_set(curves_out)
        self.outputs['Centers'].sv_set(centers_out)
        self.outputs['Radiuses'].sv_set(radius_out)


def register():
    bpy.utils.register_class(SvExCirclifyNode)


def unregister():
    bpy.utils.unregister_class(SvExCirclifyNode)
